import numpy as np
from collections import Counter
import matplotlib.pyplot as plt
#定义KNN算法的Model

class KNN():
    #初始化，设置neighbors=3,使用L2范数求距离
    def __init__(self,x_train,y_train,n_neighbors=3,p=2):
        self.x_train=x_train
        self.y_train=np.array(y_train)
        self.n=n_neighbors
        self.p=p
        self.once = 1
    def predict(self,x):
        #创建一个列表
        knn_list=[]
        for i in range(self.n):
            #求距离
            dist=np.linalg.norm(x-self.x_train[i],ord=self.p)
            #向列表knn_list添加前三个训练样本的距离和label，为元组(dist,label)
            #比如knn_list=[(dist0,0),(dist1,0),(dist2,1)]

            knn_list.append((dist,self.y_train[i]))

        #从第四个样本开始
        for i in range(self.n,len(self.x_train)):
            #找到knn_list中最大值索引
            max_index=knn_list.index(max(knn_list,key=lambda x:x[0]))
            dist = np.linalg.norm(x - self.x_train[i], ord=self.p)
            #与最大值比较，要把最大值踢出去,就是不断缩小预测样本与训练样本之间的距离
            if knn_list[max_index][0]>dist:
                knn_list[max_index]= (dist,self.y_train[i])
        self.once = 0
        #统计-看看有没有误判的类别，计算损失
        #knn列表存储了三个label值
        knn=[k[-1] for k in knn_list]
        #用key-value的形式记录label=0或1的1个数有多少个
        count_pairs=Counter(knn)
        #[0:1,1:2],得到最大计数值是label,少数服从多数原则
        max_count=sorted(count_pairs.items(),key=lambda x:x[-1])[-1][0]
        return max_count
    def score(self,x_test,y_test):
        right_count=0
        for x,y in zip(x_test,y_test):
            #调用了KNN.predict
            label=self.predict(x)
            if label==y:
                right_count+=1
        return right_count/len(x_test)
    def draw(self,x_test,y_test):
        X = np.linspace(self.x_train[:,0].min(),self.x_train[:,0].max(),100)
        Y = np.linspace(self.x_train[:,1].min(),self.x_train[:,1].max(),100)
        xx, yy = np.meshgrid(X, Y)# 生成网格数据用于画测试
        test = np.c_[xx.ravel(),yy.ravel()]
        pre1 = []
        pre2 = []
        for input in test:
            label = self.predict(input)
            pre1.append(label)
        plt.scatter(test[:,0],test[:,1],c=pre1,cmap="Oranges")
        plt.scatter(x_test[:, 0], x_test[:, 1], c=y_test)
        plt.show()